﻿using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using MidiSheetMusic;
using System.IO;

public class MidiPlayer : MonoBehaviour
{
    private MidiFile midiFile;
    private MidiDriver midiDriver;

    public TextAsset[] songs;
    private static TextAsset selectedSong;

    public List<GameObject> noteBars;
    public GameObject prefab;

    public GameObject selectScreen;
    public Transform songListContent;
    public GameObject songButton;

    public Text speedScaleText;
    public float speedScale = 1;

    private List<GameObject> noteSoundObjs;

    public Text playButtonText;

    // Use this for initialization
    void Start()
    {
        midiDriver = new MidiDriver();
        midiDriver.Start();

        noteSoundObjs = new List<GameObject>();

        DrawSongButton();
        LoadSong();
    }

    public void Play()
    {
        midiDriver.IsPlaying = !midiDriver.IsPlaying;
        playButtonText.text = midiDriver.IsPlaying ? "Stop" : "Play";
        if(!midiDriver.IsPlaying)
        {
            midiDriver.Write(new byte[3]
                {
                    0xFC,
                    0,
                    0
                }
            );
        }
    }

    void DrawNoteBar()
    {
        foreach (var track in midiFile.Tracks)
        {
            midiDriver.Write(new byte[3]
                {
                    (byte)(0xC0 | track.Notes[0].Channel),
                    (byte)track.Instrument,
                    0
                }
            );
            foreach (var note in track.Notes)
            {
                if (note.Number >= 24 && note.Number <= 108)
                {
                    int barNumber = note.Number - 24;
                    Transform content = noteBars[barNumber].transform.FindChild("Viewport/Content");
                    GameObject go = Instantiate(prefab, content) as GameObject;
                    go.GetComponent<RectTransform>().sizeDelta = new Vector2(0, note.Duration);
                    go.GetComponent<RectTransform>().localScale = Vector3.one;
                    go.GetComponent<RectTransform>().anchoredPosition = new Vector2(0, note.StartTime + 50);
                    go.GetComponent<NoteSound>().note = note;
                    Color color = midiFile.Tracks.IndexOf(track) == 0 ? Color.blue : Color.green;
                    go.GetComponent<Image>().color = color;
                    noteSoundObjs.Add(go);
                }
            }
        }
    }

    void DrawSongButton()
    {
        for (int i = 0; i < songs.Length; i++)
        {
            GameObject go = Instantiate(songButton, songListContent) as GameObject;
            go.GetComponentInChildren<UnityEngine.UI.Text>().text = songs[i].name;
            go.GetComponent<RectTransform>().localScale = Vector3.one;
            go.GetComponent<Button>().onClick.AddListener
            (
                new UnityEngine.Events.UnityAction(
                    delegate ()
                    {
                        selectedSong = songs.FirstOrDefault(x => x.name == go.GetComponentInChildren<UnityEngine.UI.Text>().text);
                        midiDriver.IsPlaying = false;
                        playButtonText.text = "Play";
                        foreach (var item in noteSoundObjs)
                        {
                            Destroy(item);
                        }
                        noteSoundObjs.Clear();
                        LoadSong();
                        selectScreen.SetActive(false);
                    }
                )
            );
        }
    }

    void LoadSong()
    {
        if (selectedSong == null)
        {
            selectedSong = songs[0];
        }

        byte[] data = selectedSong.bytes;
        midiFile = new MidiFile(data, "");

        midiDriver.Speed = speedScale * midiFile.Time.Quarter * 1000000 / midiFile.Time.Tempo;

        DrawNoteBar();
    }

    public void SetSpeedScale(float scale)
    {
        speedScale = scale;
        midiDriver.Speed = speedScale * midiFile.Time.Quarter * 1000000 / midiFile.Time.Tempo;
        speedScaleText.text = scale.ToString("F1");
    }

    private void OnApplicationQuit()
    {
        midiDriver.Stop();
    }

    private void OnApplicationPause(bool pause)
    {
        if (pause)
        {
            midiDriver.Stop();
        }
        else
        {
            if (midiDriver != null)
                midiDriver.Start();
        }
    }
}
